//
// Created by Never on 2019/10/27.
//

#ifndef RANDOM_NUMBER_H
#define RANDOM_NUMBER_H

#include <random>
#include <algorithm>
#include <type_traits>

using namespace std;

template<typename Type>
using uniform_distribution =
typename std::conditional<
        std::is_floating_point<Type>::value,
        std::uniform_real_distribution<Type>,
        typename std::conditional<
                std::is_integral<Type>::value,
                std::uniform_int_distribution<Type>,
                void
        >::type
>::type;

class RandomNumber {
private:
    random_device randomDevice;
    mt19937_64 randomEngine;
public:
    RandomNumber(const RandomNumber &randomNumber): randomEngine(randomNumber.randomEngine){}
    explicit RandomNumber(unsigned int seed): randomEngine(seed){};
    RandomNumber(): randomEngine(randomDevice()){};

    template <typename Type>
    Type getRandomNumber(Type lowerBound, Type upperBound);
    void nonDuplicatedRandomNumbers(int lowerBound, int upperBound, int numOfRandomNum, vector<int> &numbers);
    template <typename Type>
    void pickKEleAndPutThemInFirstKPosRandomly(vector<Type>& elements, int k);
    // arrange elements int the interval [start, end).
    template <typename Type>
    void arrangeElementsOfInterval(vector<Type>& elements, int start, int end);
    bool isCurEleChosen(int appearNum, int totalNum);
};


template <typename Type>
Type RandomNumber::getRandomNumber(Type lowerBound, Type upperBound) {
    uniform_distribution<Type> distribution(lowerBound, upperBound);
    return distribution(randomEngine);
}

template <typename Type>
void RandomNumber::pickKEleAndPutThemInFirstKPosRandomly(vector<Type> &elements, int k) {
    for(int start = 0; start < k; start++) {
        auto randomNum = getRandomNumber(start, static_cast<int>(elements.size())-1);
        swap(elements[start], elements[randomNum]);
    }
}

template <typename Type>
void RandomNumber::arrangeElementsOfInterval(vector<Type> &elements, int start, int end) {
    while(start < end-1) {
        auto randomNum = getRandomNumber(start, end-1);
        swap(elements[start], elements[randomNum]);
        start++;
    }
}


#endif //RANDOM_NUMBER_H
